#pragma once
#include <stdint.h>
#include <array>
#include <functional>
#include <memory>
#include <list>
#include "IntrusiveList.h"

namespace TimeWheel
{
const uint32_t TIME_WHEEL_BUCKETS    = 256;
const uint32_t TIME_WHEEL_MAX_LEVELS = 4;

const time_t   TIMER_REMAIN_INVALID  = -1;

using TimerFunc                      = std::function<void(void*)>;
using TimerFlag                      = uint32_t;

struct CTimer;
using ListEntry = IntrusiveList::ListNode<CTimer>;
using TimerPtr  = std::shared_ptr<CTimer>;

enum class TimerPeriodic : bool
{
    TP_FALSE,
    TP_TRUE,
};

enum TIMER_FLAG
{
    TF_NONE      = 0x0,
    TF_SCHEDULED = 0x1,    // added to time wheel
    TF_STOPED    = 0x2,    // Manually stopped
    TF_PERIODIC  = 0x4,    // Periodic timer
};

class CTimeWheel;

struct CTimer : public std::enable_shared_from_this<CTimer>
{
public:
    static TimerPtr CreateNew();

    ~CTimer();

    bool Start(CTimeWheel*   pTimeWheel,
               time_t        tTimeout,
               TimerFunc     func,
               void*         arg,
               TimerPeriodic ePeriodic = TimerPeriodic::TP_FALSE);
    bool Stop();
    bool IsFlag(TimerFlag dwTestFlag);
    void SetFlag(TimerFlag dwFlagBit);
    void ClrFlag(TimerFlag dwFlagBit);

public:
    ListEntry entry;
    time_t    tRemaining;
    time_t    tInterval;
    time_t    tStartTime;
    void*     arg;
    TimerFunc f;
    TimerFlag dwFlag;

private:
    //do not using CTimer directly, use TimerPtr instead
    CTimer();
    std::shared_ptr<CTimer> GetPtr();

    struct MakeSharedEnabler;
};

struct CTimer::MakeSharedEnabler : public CTimer
{
    MakeSharedEnabler() : CTimer() {}
};

class CTimeWheel
{
private:
    CTimeWheel();
    ~CTimeWheel();

public:
    static CTimeWheel* CreateNew();
    bool               Create(time_t tNow);
    void               Release();

public:
    bool   StartTimer(TimerPtr& pTimer);
    bool   StopTimer(TimerPtr& pTimer);
    void   RunTicks(time_t tNow);
    time_t GetRemain(TimerPtr& pTimer);

private:
    using TmpTimerList = std::list<std::weak_ptr<CTimer>>;
    using TickCallBack = std::function<void(TimerPtr&, TmpTimerList&)>;

    enum class TickTmpListRule
    {
        OnTick,
        OnDone,
    };
    void        DoTick(time_t nOffset, std::function<void(TimerPtr&, TmpTimerList&)> func, TickTmpListRule eRule);
    void        RunTick(time_t tNow);
    void        FastForward(time_t tNow);
    time_t      CalcRemainTimeout(time_t tNow, time_t tInterval);
    static bool IsValidPeriodicTimer(const TimerPtr& pTimer);

    using List   = IntrusiveList::List<CTimer>;
    using Bucket = std::array<List, TIME_WHEEL_BUCKETS>;

    typedef struct Wheel_s
    {
        uint32_t nHand;
        Bucket   buckets;
    } Wheel_t;

    using TimeWheel = std::array<Wheel_t, TIME_WHEEL_MAX_LEVELS>;

    TimeWheel m_arrWheels;
    time_t    m_tLastRun;
};
}    // namespace TimeWheel
